﻿using System;
using System.Collections.Generic;
using System.Text;
using PacMan3D.IO;
using PacMan3D.Objects;
using PacMan3D.Utils;
using PacMan3D.Sounds;

namespace PacMan3D.Physics
{
    public class CollisionEngine
    {
        private Maze Maze;

        public CollisionEngine(Maze maze)
        {
            this.Maze = maze;
        }

        public void CheckForCollisios()
        {
            foreach (Ghost ghost in this.Maze.GhostObjects)
                CheckForGhostCollision(ghost);

            CheckForPacManCollision();
        }

        private Point3D positionAhead, positionALeft, positionARight;
        private void CheckForGhostCollision(Ghost ghost)
        {
            positionAhead = ghost.GetPositionAhead();
            
            // PacMan Search
            if (this.Maze.PacMan.IsAlive && !this.Maze.PacMan.IsBorning)
                if (IsPoint3DInside(positionAhead, this.Maze.PacMan))
                    if (ghost.CollidesWithPacMan(this.Maze.PacMan))
                    {
                        ghost.PacManCollision();
                        this.Maze.GhostCollision();
                    }
            
            ///
            /// Wall Boxes Search
            /// 
            foreach (WallBox wall in this.Maze.WallBoxObjects)
            {
                if (IsPoint3DInside(positionAhead, wall))
                    if (ghost.CollidesWithWall(wall))
                    {
                        ghost.WallColision();
                        return;
                    }
            }

            ///
            /// Empty Boxes Search
            /// 
            positionALeft = ghost.GetPositionALeft();
            positionARight = ghost.GetPositionARight();
            foreach (EmptyBox emptyBox in this.Maze.EmptyBoxObjects)
            {
                if (positionALeft.IsEqualXZ(emptyBox.Position) || positionARight.IsEqualXZ(emptyBox.Position))
                {
                    ghost.EmptyBoxCollision();
                    return;
                }
            }

            ///
            /// Other Ghosts Search
            /// 
            foreach (Ghost oGhost in this.Maze.GhostObjects)
                if (ghost.GhostNumber != oGhost.GhostNumber)
                    if (IsPoint3DInside(ghost.Position, oGhost))
                        if (ghost.CollidesWithOtherGhost(oGhost))
                        {
                            ghost.GhostCollision();
                            oGhost.GhostCollision();
                        }
        }

        private void CheckForPacManCollision()
        {
            List<ICollidable> objects = GetObjectsToTestCollision();

            foreach (ICollidable obj in objects)
            {
                if (this.Maze.PacMan.Collides(obj))
                {
                    if (obj is WallBox)
                        this.Maze.PacMan.MoveBack();
                    else if (obj is Life)
                        this.Maze.LifeCollision((Life)obj);
                    else if (obj is Food)
                        this.Maze.FoodCollision((Food)obj);
                    else if (obj is BigFood)
                        this.Maze.BigFoodCollision((BigFood)obj);
                }
            }
        }

        private List<ICollidable> GetObjectsToTestCollision()
        {
            PacMan pacman = this.Maze.PacMan;
            Point3D positionAhead = pacman.GetPositionAhead();

            List<ICollidable> objects = new List<ICollidable>();

            foreach (WallBox wall in this.Maze.WallBoxObjects)
                if (IsPoint3DInside(positionAhead, wall))
                    objects.Add(wall);

            foreach (Food food in this.Maze.FoodObjects)
                if (IsPoint3DInside(positionAhead, food))
                    objects.Add(food);

            foreach (BigFood bigfood in this.Maze.BigFoodObjects)
                if (IsPoint3DInside(positionAhead, bigfood))
                    objects.Add(bigfood);

            foreach (Life life in this.Maze.LifeObjects)
                if (IsPoint3DInside(positionAhead, life))
                    objects.Add(life);

            return objects;
        }

        public bool IsPoint3DInside(Point3D point3D, ICollidable obj)
        {
            return (Math.Abs(point3D.X - obj.Position.X) <= this.Maze.Platform.PieceSize 
                && Math.Abs(point3D.Z - obj.Position.Z) <= this.Maze.Platform.PieceSize);
        }
    }
}
